李守中

Git 相关

Table of Contents

1. 代理

Github 上的仓库要挂代理才能被 git clone ,下面是配置 socks5 代理的命令:

git config --global http.proxy "socks5://127.0.0.1:10808"
git config --global https.proxy "socks5://127.0.0.1:10808"

2. 好用的命令

2.1. 重置整个分支所有 commit 的作者

git rebase -r --root --exec "git commit --amend --no-edit --reset-author"

2.2. 删除远程仓库中的分支

git push <remote-name> --delete <branch-name>

2.3. 合并 commit

操作从第一个 commit 开始的所有 commit:

git rebase --interactive --root

从指定 id 的 commit 开始操作:

git rebase --interactive <commit-id>

3. 撤销修改

3.1. checkout 命令

有两种工作方式,参数带路径和不带路径。

3.1.1. git checkout <commit-or-branch>

git checkout <commit-or-branch> 用于切换到某个提交或分支的引用。这个操作对工作区的数据安全。

命令的行为是:

  • HEAD 指针指向命令中所写的 commit 快照或者分支的引用 ( 比如 master )
  • 将该 commit 快照更新到工作区和暂存区

对于工作区和暂存区来说:

  • 如果工作区和暂存区没有未 commit 的数据,则工作区、暂存区的数据会和该 commit 快照保持一致
  • 如果工作区或暂存区有未 commit 的数据:
    • 尝试合并本地数据和文件快照
    • 如果合并失败,终止 checkout 命令并退回到命令执行之前的状态

3.1.2. git checkout <commit-id> </path/to/file>

git checkout <commit-id> </path/to/file> 用于将工作区或/和暂存区的数据恢复到某个历史版本。

不移动 HEAD 指针。这个操作对工作区的数据不安全。 它会从 <commit-id> 中找到指定文件的快照,将其 覆盖 到工作区与暂存区。 若工作区或/和暂存区存在对该文件的更改,则更改会被丢弃。

3.1.3. git checkout -- <filename>

注: -- 表示该符号之后的字符串将不会被解析成参数。

git checkout -- <filename> 用于将工作区的文件恢复到上一次快照时的状态。

  • 如果文件在被修改后,没有被放到暂存区,文件就回到和版本库中 HEAD 指针指向的提交一模一样的状态
  • 如果文件在被修改后,被添加到了暂存区,在用暂存区的快照覆盖掉工作区的文件后,删除暂存区的快照

不移动 HEAD 指针。这个操作对工作区的数据不安全。

3.2. switch 命令

switch 命令专门用于切换分支,可以用来替代 checkout 的部分用途。

git switch -c <new-branch> 创建并切换到指定分支。

git switch <branch> 切换到已有分支。

和 checkout 一样, switch 对工作区是安全的,它会尝试合并本地数据 ( 工作区和暂存区的数据 ) 和文件快照,如果无法合并则中止操作,回到命令执行之前的状态。

3.3. reset 命令

git reset <commit-id> 的主要作用是让 HEAD 指针指向特定的 commit。

与 checkout 的区别在于,它对提交历史的更改并不仅仅只是更新 HEAD 本身,如果 HEAD 原来指向某个分支引用 ( 比如 master 分支 ),则会将分支引用 ( master ) 也指向该 commit。

主要了解三个参数:

  • --soft 更新提交历史
  • --mixed 更新提交历史和暂存区 ( 默认 )
  • --hard 更新提交历史、暂存区和工作区

3.3.1. --soft

如果 HEAD 指针指向 master,则命令执行后 HEAD 依然指向 master,但是 master 指向指定的 commit。

如果 HEAD 指针直接指向某个 commit,则 master 不改变指向,HEAD 指针重新指向指定的 commit。

它不会对暂存区和工作区的数据做任何修改。

3.3.2. --mixed

是默认选项。 git reset --mixed <commit-id> 等同于 git reset <commit-id>

它在 --soft 所做操作的基础上,再把 HEAD 指针指向的 commit 快照 覆盖 到暂存区。

它不会对工作区的数据做任何修改。

所以可以用 git reset HEAD <filename> 把某个文件快照从暂存区中删除 ( unstage )。

reset 带文件路径的完整形式是: git reset [<tree-ish>] <pathspec>

该操作的实质是从 <tree-ish> 提取 <pathspec> ( 某个文件 ) 对应的快照覆盖到暂存区。 <tree-ish> 可以是 commit 或分支,默认值 HEAD,所以可以实现从暂存区删除指定文件快照的效果。

3.3.3. --hard

这个操作对工作区的数据不安全。

它在 --mixed 所作操作的基础上,再把 HEAD 指针指向的 commit 快照 覆盖 到工作区。工作区和暂存区中所有未提交的更改都会永久丢失。

reset 后丢失的提交历史仍然能够恢复,因为被更新的只有 HEAD 指针,没有对实际的提交对象做任何更改。可以通过 git reflog 找到 HEAD 曾经指向过的 commit ID 后再 git reset --hard <commit-id> 回到该 commit 上。

3.4. restore 命令

restore 命令用于还原工作区,或工作区和暂存区中的指定文件或文件集合,格式为:

git restore [--source=<tree>] [--staged] [--worktree] <pathspec>…

  • --source 参数指定某个 commit ID,默认值是 HEAD 指针指向的 commit
  • <pathspec> 指定一个或多个文件
  • 对于 --staged--worktree 两个参数:
    • --worktree 这个命令对工作区生效
    • --staged 这个命令对暂存区生效
    • 两个参数同时使用 --worktree --staged--source 参数指定的 commit 覆盖工作区和暂存区
    • 只使用一个参数:
      • --worktree 参数是默认值,可以不写
      • --staged 参数被指定时,默认的 --worktree 参数不生效

它不会移动 HEAD 指针。这个操作对工作区的数据不安全。

git restore [--source=<tree-ish>] --staged <pathspec>... 等同于 git reset [<tree-ish>] <pathspec> 。较新版本的 Git 会在命令行中提示使用 restore 命令来取消暂存或丢弃工作区的改动。

3.5. revert 命令

它用于撤销已被 commit 的数据。

checkout 和 reset 命令会将 HEAD 指针或分支引用重新指向指定的 commit,而 revert 会将指定 commit 在当前分支上重新提交一次。这个操作对工作区的数据安全。

命令的行为是:

  • 如果工作区或/和暂存区有未 commit 的数据,终止 revert 命令并退回到命令执行之前的状态
  • 如果工作区或/和暂存区没有未 commit 的数据
    • 将指定的 commit 覆盖到工作区
    • 将工作区的数据添加到暂存区
    • 创建新的提交

比如 git revert <commit2> 会有如下效果:

...<-[commit1]<-[commit2]<-[commit3]<-master<-HEAD

$ git revert <commit2>

...<-[commit1]<-[commit2]<-[commit3]<-[commit2]<-master<-HEAD

4. 子模块

4.1. 删除子模块

要删除的总共有 5 个地方:

  1. .gitmodules 文件中的记录
  2. .git/config 文件中的记录
  3. .git/modules 目录下子模块的 git 仓库
  4. 子模块的工作区目录
  5. 暂存区中子模块的记录

其实,删除前 4 个之后,子模块实质上就已经被删除了。此时查看 git status 会发现这样的记录:

new file: <path/to/module>
deleted:  <path/to/module>

意思是,这个文件在 git module add 的时候被加入到暂存区,多出一条 new file 的记录;然后这个文件又被删除,又多了一条 deleted 记录。

但如果不删除依旧保存在暂存区中的,已经被删除的子模块的记录,会导致无法重新添加这个子模块 ( 因为暂存区有这个子模块的记录 )。所以必须先从暂存区删除这个子模块的记录,才能重新添加这个子模块。

手动删除流程:

  1. 删除暂存区的 submodule 数据 git rm --cached path/to/submodule
  2. 在 .gitmodules 文件中删除对应 submodule 记录并将其加入暂存区
  3. 在 .git/config 中删除对应 submodule 记录
  4. 删除 submodule 的 git 仓库 rm -rf .git/modules/<path/to/submodule>
  5. 删除硬盘上的 submodule 工作区目录 rm -rf path/to/submodule

注意,如果把 git rm --cached ... 放在最后做,则 git 会提示要先把 .gitmodules 文件加入暂存区才能进行下一步操作。

git submodule deinit -f path/to/submodule 可以清空子模块的工作区,但保留工作区目录。



Last Update: 2024-03-10 Sun 21:56

Generated by: Emacs 28.2 (Org mode 9.5.5)   Contact: [email protected]

若正文中无特殊说明,本站内容遵循: 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议